import socket
import os
import json
import struct
import logging

class SimpleFileClient:
    """一个简单的文件传输客户端"""
    def __init__(self, host = 'localhost', port = 8080, download_dir = 'D://MyCode//python_project//Python-Network-Programming//Best Practice//文件传输//面向对象版//client//download'):
        """        
        :param host: 服务器地址
        :param port: 服务器端口
        :param download_dir: 下载文件保存目录路径
        """

        self.host = host
        self.port = port
        self.download_dir = download_dir  # 修复成员变量名
        self.client_socket = None
        self.logger = self.setup_logger()
        
        # 创建下载目录
        self.create_directory(download_dir)
    def setup_logger(self):
        """设置简单的日志记录器"""
        logger = logging.getLogger('FileClient')
        logger.setLevel(logging.INFO)
        
        # 创建控制台处理器
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.INFO)
        
        # 设置日志格式
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        console_handler.setFormatter(formatter)
        
        logger.addHandler(console_handler)
        return logger
    
    def create_directory(self, path):
        """创建目录（如果不存在）"""
        try:
            os.makedirs(path, exist_ok=True)
            self.logger.info(f"下载目录已创建: {path}")
        except Exception as e:
            self.logger.error(f"创建目录失败: {str(e)}")
            raise
    def connect(self):
        """连接到服务器"""
        try:
            #创建客户端套接字对象
            self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            #与服务端进行连接 - 修复递归调用错误
            self.client_socket.connect((self.host, self.port))
            self.logger.info(f"已连接到服务器 {self.host}:{self.port}")
            return True
        except Exception as e:
            self.logger.error(f"连接服务器失败: {str(e)}")
            return False
    
        
    def disconnect(self):
        """断开服务器连接"""
        if self.client_socket:
            try:
                self.client_socket.close()
                self.logger.info('连接已关闭')
            except Exception as e:
                self.logger.error(f'关闭连接时出错：{str(e)}')
        

    def run(self):
        """运行客户端"""
        if not self.client_socket:
            return
        
        try:
            self.logger.info("客户端已就绪 (输入 'help' 查看帮助)")
            while True:
                #处理用户输入
                command = input(">>")
                # 本地退出命令
                if command == 'exit':
                    self.logger.info("退出客户端")
                    break
                # 本地帮助命令
                if command == 'help':
                    self.show_help()
                    continue  # 新增：跳过服务器通信
                # 仅发送服务器命令
                self.client_socket.send(command.encode('utf-8'))
                # 处理服务器响应
                self.handle_response()
        except KeyboardInterrupt:
            self.logger.info("操作中断")
        except Exception as e:
            self.logger.error(f"客户端错误: {str(e)}")
        finally:
            self.disconnect()
          
    def show_help(self):
        """显示帮助信息"""
        help_text = """
        可用命令:
          get <文件名>  - 下载文件
          list         - 列出服务器上的文件
          help         - 显示帮助信息
          exit         - 退出客户端
        """
        self.logger.info(help_text)

    def handle_response(self):
        """处理服务器响应"""
        try:
            #接受响应头长度
            header_length_data = self.receive_data(4)
            if len(header_length_data) != 4:
                self.logger.error("响应头长度接收失败")
                return
            #解析响应头长度
            header_length = struct.unpack('>I', header_length_data)[0]
            #接收响应头数据
            header_data = self.receive_data(header_length)
            #解析json响应头
            try:
                header = json.loads(header_data.decode('utf-8'))
            except Exception as e:
                self.logger.error(f"解析响应头失败: {str(e)}")
                return
            #根据响应类型处理
            response_type = header.get("type", "")
            
            if response_type == "file":
                self.handle_file_response(header)
            elif response_type == "file_list":
                self.handle_file_list_response(header)
            elif response_type == "error":
                self.handle_error_response(header)
            else:
                self.logger.error(f"未知响应类型: {response_type}")
        except Exception as e:
            self.logger.error(f"处理响应时出错: {str(e)}")
    def handle_error_response(self, header):
        """处理错误类型响应"""
        error_message = header.get('message', '未知错误')
        self.logger.error(f"服务器返回错误: {error_message}")
        
    def receive_data(self, size):
        """接受固定字节数"""
        data = b''
        received = 0  # 修复变量名和初始值

        while received < size:  # 修复循环条件
            #计算还需接收多少字节数
            remaining = size - received
            #最多一次接受1024字节数
            chunk_size = 1024 if remaining > 1024 else remaining 
            try:
                #接受字节
                chunk = self.client_socket.recv(chunk_size)
                if not chunk:
                    break
                data += chunk
                received += len(chunk)  # 修复累加方式
            except Exception as e:
                self.logger.error(f"接收数据失败: {str(e)}")
                break
        return data
    
    def handle_file_response(self, header):
        """处理文件类型响应"""
        try:
            #获取文件信息
            filename = header.get('filename', 'unkonw')
            file_size = header.get('file_size', 0)
            if file_size <= 0:
                self.logger.warning("收到空文件")
                return
            self.logger.info(f"开始接收文件: {filename} ({file_size} 字节)")
            #接受文件内容
            file_data = self.receive_data(file_size)
            if len(file_data) != file_size:
                self.logger.error(f"文件接收不完整: {len(file_data)}/{file_size} 字节")
                return
            #保存文件
            self.save_file(filename, file_data)
        except Exception as e:
            self.logger.error(f"处理文件响应失败: {str(e)}")

    def save_file(self, filename, file_data):
        """保存文件到下载目录"""
        try:
            # 创建安全文件名（移除路径字符）
            safe_filename = "".join(c for c in filename if c not in ['/', '\\', ':', '*', '?', '"', '<', '>', '|'])
                
            # 完整文件路径
            file_path = os.path.join(self.download_dir, safe_filename)
                
            # 写入文件
            with open(file_path, "wb") as file:
                file.write(file_data)
                
            self.logger.info(f"文件保存成功: {file_path}")
        except Exception as e:
            self.logger.error(f"保存文件失败: {str(e)}")
    

if __name__ == "__main__":
    # 创建并运行客户端
    client = SimpleFileClient(
        host="localhost",
        port=8080,
        download_dir="D://MyCode//python_project//Python-Network-Programming//Best Practice//文件传输//面向对象版//client//download"  # 客户端下载目录
    )
    client.connect()  # 新增：建立服务器连接
    client.run()